import mongoose from 'mongoose';
import { createClient, RedisClientType } from 'redis';
import config from '@config/index';
import logger from '@utils/logger';

class Database {
  private static instance: Database;
  private mongoConnection: typeof mongoose | null = null;
  private redisClient: RedisClientType | null = null;

  private constructor() {}

  public static getInstance(): Database {
    if (!Database.instance) {
      Database.instance = new Database();
    }
    return Database.instance;
  }

  // MongoDB 连接
  public async connectMongoDB(): Promise<void> {
    try {
      const options = {
        // 连接池配置
        maxPoolSize: 10,
        serverSelectionTimeoutMS: 5000,
        socketTimeoutMS: 45000,
        family: 4, // 使用 IPv4
      };

      this.mongoConnection = await mongoose.connect(config.env.MONGODB_URI, options);

      // 监听连接事件
      mongoose.connection.on('connected', () => {
        logger.info('MongoDB connected successfully');
      });

      mongoose.connection.on('error', (error) => {
        logger.error('MongoDB connection error:', error as Error);
      });

      mongoose.connection.on('disconnected', () => {
        logger.warn('MongoDB disconnected');
      });

      // 优雅关闭
      process.on('SIGINT', this.gracefulShutdown.bind(this));
      process.on('SIGTERM', this.gracefulShutdown.bind(this));

      logger.info('MongoDB connection established');
    } catch (error) {
      logger.error('Failed to connect to MongoDB:', error as Error);
      throw error;
    }
  }

  // Redis 连接
  public async connectRedis(): Promise<void> {
    try {
      this.redisClient = createClient({
        url: config.env.REDIS_URI,
        socket: {
          reconnectStrategy: (retries) => {
            // 重连策略：最大重试10次，每次间隔递增
            if (retries > 10) {
              return new Error('Redis connection failed after 10 retries');
            }
            return Math.min(retries * 100, 3000);
          },
        },
      });

      // 监听连接事件
      this.redisClient.on('connect', () => {
        logger.info('Redis connecting...');
      });

      this.redisClient.on('ready', () => {
        logger.info('Redis connected and ready');
      });

      this.redisClient.on('error', (error) => {
        logger.error('Redis connection error:', error as Error);
      });

      this.redisClient.on('end', () => {
        logger.warn('Redis connection ended');
      });

      await this.redisClient.connect();
      logger.info('Redis connection established');
    } catch (error) {
      logger.error('Failed to connect to Redis:', error as Error);
      throw error;
    }
  }

  // 获取 Redis 客户端
  public getRedisClient(): RedisClientType {
    if (!this.redisClient) {
      throw new Error('Redis client not initialized');
    }
    return this.redisClient;
  }

  // 检查数据库连接状态
  public async checkConnections(): Promise<{
    mongodb: boolean;
    redis: boolean;
  }> {
    const status = {
      mongodb: false,
      redis: false,
    };

    try {
      // 检查 MongoDB
      if (this.mongoConnection && mongoose.connection.readyState === 1) {
        await mongoose.connection.db?.admin().ping();
        status.mongodb = true;
      }
    } catch (error) {
      logger.error('MongoDB health check failed:', error as Error);
    }

    try {
      // 检查 Redis
      if (this.redisClient && this.redisClient.isReady) {
        await this.redisClient.ping();
        status.redis = true;
      }
    } catch (error) {
      logger.error('Redis health check failed:', error as Error);
    }

    return status;
  }

  // 优雅关闭连接
  private async gracefulShutdown(): Promise<void> {
    logger.info('Received shutdown signal, closing database connections...');

    try {
      // 关闭 MongoDB 连接
      if (this.mongoConnection) {
        await mongoose.connection.close();
        logger.info('MongoDB connection closed');
      }

      // 关闭 Redis 连接
      if (this.redisClient) {
        await this.redisClient.quit();
        logger.info('Redis connection closed');
      }

      logger.info('All database connections closed gracefully');
      process.exit(0);
    } catch (error) {
      logger.error('Error during graceful shutdown:', error as Error);
      process.exit(1);
    }
  }

  // 清理测试数据（仅在测试环境使用）
  public async clearTestData(): Promise<void> {
    if (config.env.NODE_ENV !== 'test') {
      throw new Error('clearTestData can only be used in test environment');
    }

    try {
      // 清理 MongoDB 测试数据
      if (this.mongoConnection) {
        const collections = await mongoose.connection.db?.collections();
        if (collections) {
          await Promise.all(
            collections.map(collection => collection.deleteMany({}))
          );
        }
      }

      // 清理 Redis 测试数据
      if (this.redisClient) {
        await this.redisClient.flushDb();
      }

      logger.info('Test data cleared successfully');
    } catch (error) {
      logger.error('Failed to clear test data:', error as Error);
      throw error;
    }
  }
}

export default Database.getInstance();